package cn.hj.config;

import cn.hj.types.annotatioins.DCCValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.CuratorCache;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
@Slf4j
public class DCCValueBeanFactory implements BeanPostProcessor {

    private static final String BASE_CONFIG_PATH = "/big-market-dcc";
    private static final String BASE_CONFIG_PATH_CONFIG = BASE_CONFIG_PATH + "/config";

    private final CuratorFramework client;

    private final Map<String, Object> dccObjGroup = new HashMap<>();

    public DCCValueBeanFactory(CuratorFramework client) throws Exception {
        this.client = client;
        if (null == client.checkExists().forPath(BASE_CONFIG_PATH)) {
            client.create().creatingParentsIfNeeded().forPath(BASE_CONFIG_PATH);
        }

        CuratorCache curatorCache = CuratorCache.build(client, BASE_CONFIG_PATH_CONFIG);
        curatorCache.start();

        curatorCache.listenable().addListener((type, oldData, data) -> {
            switch (type) {
                case NODE_CHANGED:
                    String dccValuePath = data.getPath();
                    Object objBean = dccObjGroup.get(dccValuePath);
                    if (null == objBean) return;
                    try {
                        // 1. getDeclaredField 方法用于获取指定类中声明的所有字段，包括私有字段、受保护字段和公共字段。
                        // 2. getField 方法用于获取指定类中的公共字段，即只能获取到公共访问修饰符（public）的字段。
                        Field field = objBean.getClass().getDeclaredField(dccValuePath.substring(dccValuePath.lastIndexOf("/") + 1));
                        field.setAccessible(true);
                        field.set(objBean, new String(data.getData()));
                        field.setAccessible(false);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    break;
                default:
                    break;
            }
        });
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> beanClass = bean.getClass();
        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (!declaredField.isAnnotationPresent(DCCValue.class)) {
                continue;
            }
            DCCValue dccValue = declaredField.getAnnotation(DCCValue.class);
            String value = dccValue.value();
            if (StringUtils.isBlank(value)) {
                throw new RuntimeException("DCCValue注解的value不能为空");
            }
            String[] split = value.split(":");
            String key = split[0];
            String defaultValue = split.length > 1 ? split[1] : "";

            String keyPath = BASE_CONFIG_PATH_CONFIG.concat("/").concat(key);
            try {
                if (null == client.checkExists().forPath(keyPath)) {
                    client.create().creatingParentsIfNeeded().forPath(keyPath);
                    if (!StringUtils.isBlank(defaultValue)) {
                        declaredField.setAccessible(true);
                        declaredField.set(bean, defaultValue);
                        declaredField.setAccessible(false);
                    }
                } else {
                    String configValue = Arrays.toString(client.getData().forPath(keyPath));
                    if (!StringUtils.isBlank(configValue)) {
                        declaredField.setAccessible(true);
                        declaredField.set(bean, configValue);
                        declaredField.setAccessible(false);
                        log.info("DCC 节点监听 设置配置 {} {} {}", keyPath, declaredField.getName(), configValue);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            dccObjGroup.put(keyPath, bean);
        }
        return bean;
    }
}
